Skip to content

feat(metrics): Add implementation for metrics envelope item#6960

Merged
philprime merged 102 commits intomainfrom
philprime/metrics-envelope
Jan 15, 2026
Merged

feat(metrics): Add implementation for metrics envelope item#6960
philprime merged 102 commits intomainfrom
philprime/metrics-envelope

Conversation

@philprime
Copy link
Member

@philprime philprime commented Dec 2, 2025

This PR is part of a merge-chain and should be merged one-by-one into main as soon as all of them are ready to be merged:

  1. feat(metrics): Add integration with installation by SDK #6956
  2. feat(metrics): Add implementation for metrics envelope item #6960
  3. feat(metrics): Add public API to collect count, distribution and gauge #6957

📜 Description

This PR implements the metrics envelope item functionality for the Sentry Cocoa SDK. It adds the core infrastructure for capturing, batching, and sending metrics to Sentry.

Key Changes

  • SentryMetric Protocol: Added a new SentryMetric class that represents a metric entry with support for:

    • Three metric types: counter, gauge, and distribution
    • Timestamp, trace ID, and optional span ID for distributed tracing
    • Metric name, value, unit, and structured attributes
    • JSON encoding support for envelope serialization
  • SentryMetricBatcher: Implemented a batcher that:

    • Batches metrics together before sending (configurable max count: 100, max buffer size: 1MB)
    • Flushes metrics automatically after a timeout (default: 5 seconds) or when limits are reached
    • Enriches metrics with scope attributes (environment, release, user info, scope attributes)
    • Sets trace ID from propagation context and span ID from active span
    • Supports beforeSendMetric callback for filtering/modifying metrics
  • MetricsIntegration Updates: Enhanced the integration to:

    • Initialize and manage the metric batcher
    • Provide addMetric(_:scope:) API for adding metrics
    • Flush pending metrics on uninstall
  • Client Support: Added captureMetricsData(_:with:) method to SentryClient that:

    • Creates envelope items with type trace_metric
    • Uses content type application/vnd.sentry.items.trace-metric+json
    • Includes item count in the envelope item header
  • Data Category Support: Added trace_metric data category to:

    • SentryDataCategory enum
    • SentryDataCategoryMapper for mapping envelope item types to categories
    • SentryEnvelopeItemType for the envelope item type constant
  • Options: Added beforeSendMetric callback option for filtering/modifying metrics before sending

Testing

Added comprehensive test coverage:

  • SentryMetricTests: Tests for metric creation, encoding, attribute management, and metric type handling
  • SentryMetricBatcherTests: Tests for batching behavior, timeout handling, buffer size limits, attribute enrichment, scope integration, and beforeSendMetric callback
  • MetricsIntegrationTests: Tests for integration initialization and metric addition
  • SentryClientTests: Tests for captureMetricsData envelope creation

💡 Motivation and Context

Closes #6948
Closes #6951
Closes #6952

💚 How did you test it?

  • Added comprehensive unit tests for SentryMetric covering encoding, attribute management, and metric types
  • Added extensive unit tests for SentryMetricBatcher covering batching, flushing, timeout behavior, attribute enrichment, and edge cases
  • Added integration tests for MetricsIntegration and SentryClient metrics capture
  • Verified envelope item creation with correct type and content type
  • Tested data category mapping for trace_metric

📝 Checklist

You have to check all boxes before merging:

  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 2, 2025

Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against 0ebf819

@codecov
Copy link

codecov bot commented Dec 2, 2025

Codecov Report

❌ Patch coverage is 92.59259% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.472%. Comparing base (2f28bda) to head (0ebf819).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
Sources/Sentry/SentryClient.m 66.666% 4 Missing ⚠️
Sources/Sentry/SentryDataCategoryMapper.m 33.333% 4 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@              Coverage Diff              @@
##              main     #6960       +/-   ##
=============================================
+ Coverage   84.868%   85.472%   +0.603%     
=============================================
  Files          462       465        +3     
  Lines        27936     28036      +100     
  Branches     12351     12392       +41     
=============================================
+ Hits         23709     23963      +254     
+ Misses        4186      4031      -155     
- Partials        41        42        +1     
Files with missing lines Coverage Δ
SentryTestUtils/Sources/TestClient.swift 86.086% <100.000%> (+0.501%) ⬆️
...ft/Integrations/Metrics/SentryMetricsBatcher.swift 100.000% <100.000%> (ø)
...ntegrations/Metrics/SentryMetricsIntegration.swift 100.000% <100.000%> (ø)
Sources/Swift/Protocol/SentryAttribute.swift 89.583% <ø> (ø)
Sources/Swift/Protocol/SentryMetric.swift 100.000% <100.000%> (ø)
Sources/Swift/Protocol/SentryMetricValue.swift 100.000% <100.000%> (ø)
Sources/Swift/SentryDependencyContainer.swift 98.290% <ø> (ø)
Sources/Swift/SentryExperimentalOptions.swift 75.000% <ø> (ø)
Sources/Swift/Tools/Batcher/BatcherScope.swift 100.000% <100.000%> (ø)
Sources/Sentry/SentryClient.m 98.185% <66.666%> (+0.758%) ⬆️
... and 1 more

... and 13 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 2f28bda...0ebf819. Read the comment docs.

@philprime philprime changed the base branch from philprime/metrics-bootstrap to philprime/metrics-stage-1 December 2, 2025 15:06
@philprime philprime changed the base branch from philprime/metrics-stage-1 to philprime/metrics-bootstrap December 2, 2025 15:08
@philprime philprime force-pushed the philprime/metrics-bootstrap branch from a5056fb to 1210307 Compare December 2, 2025 15:13
@philprime philprime force-pushed the philprime/metrics-envelope branch 2 times, most recently from 02c3573 to 303d533 Compare December 2, 2025 15:25
@philprime philprime self-assigned this Dec 3, 2025
philprime and others added 8 commits December 9, 2025 14:58
- Introduced new test file `SentryItemBatcherTests.swift` to validate the behavior of the `SentryItemBatcher`.
- Added tests for various scenarios including item addition, buffer size limits, timeout handling, and attribute enrichment.
- Updated project configuration to include the new test file in the build settings.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Updated `TestCurrentDateProvider` to include `_absoluteTime` tracking during date advancements.
- Modified `Batcher` to clarify synchronous behavior in documentation and ensure correct item count is passed to the callback.
- Added tests to verify scope attribute application and item count during capture in `BatcherScopeTests` and `BatcherTests`.
@philprime
Copy link
Member Author

@sentry review
@cursor review

Copy link
Member

@philipphofmann philipphofmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks

Copy link
Member

@philipphofmann philipphofmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, LGTM.

@philprime
Copy link
Member Author

Thanks @philipphofmann, I'll keep this PR open and ready-for-merge until #6957 is ready to be merged too (blocked by #7077) so I merge all three PRs directly after another.

…teContent directly

- Changed the type alias for attributes in SentryMetric from SentryAttribute to SentryAttributeContent.
- Simplified the attributes dictionary handling in SentryMetric by removing unnecessary mapping.
- Updated tests to reflect the new attribute handling, ensuring consistency across metric creation and attribute assignment.
When a metric is emitted while a transaction or span is active, the metric's traceId was incorrectly sourced from the propagationContext, while span_id was correctly sourced from the active span. This caused a mismatch when the propagationContext had a different traceId than the active span, breaking correlation between metrics and their originating transaction in distributed tracing scenarios.

The fix ensures that when a span is active, traceId comes from the span (consistent with span_id), otherwise falls back to propagationContext traceId.

Added test cases to verify this behavior.
…alizer

Updated the assignment of the custom attribute in SentryMetric to utilize the .string initializer instead of the .init method, ensuring consistency with the new attribute handling approach.
The test was failing because it created a span without setting its traceId.
Since the fix now uses span.traceId when a span is active, the test needs
to set the span's traceId to match the propagationContext traceId for
consistency.
Base automatically changed from philprime/metrics-bootstrap to main January 15, 2026 17:30
@philprime philprime enabled auto-merge (squash) January 15, 2026 17:39
@github-actions
Copy link
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

Metrics

  • Add implementation for metrics envelope item by philprime in #6960
  • Add integration with installation by SDK by philprime in #6956

Other

  • Add isiOSAppOnVisionOS, isiOSAppOnMac, isMacCatalystApp to device context by philprime in #6939

Bug Fixes 🐛

  • (logs) Use sendDefaultPii and span_id for attributes by philprime in #7055
  • Fix incorrect variable assignment for 'sampled' key by xjshi in #7120
  • Mark dark theme deprecated by noahsmartin in #7114
  • Update raw_description in runtime context for Mac Catalyst App by philprime in #7082
  • Use correct parsing for stackframes by noahsmartin in #6908
  • Transport correctly handling 4xx and 5xx by dfed in #6618

Build / dependencies / internal 🔧

Deps

  • Bump ruby/setup-ruby from 1.279.0 to 1.281.0 by dependabot in #7160
  • Bump getsentry/craft from 2.18.1 to 2.18.3 by dependabot in #7161
  • Bump getsentry/craft/.github/workflows/changelog-preview.yml from 2.18.1 to 2.18.3 by dependabot in #7159
  • Bump ruby/setup-ruby from 1.276.0 to 1.279.0 by dependabot in #7117
  • Bump mikepenz/action-junit-report from 6.0.1 to 6.1.0 by dependabot in #7116
  • Update swiftlint version by github-actions in #7109
  • Bump ruby/setup-ruby from 1.275.0 to 1.276.0 by dependabot in #7103
  • Bump codecov/test-results-action from 1.1.1 to 1.2.1 by itaybre in #7087
  • Bump ruby/setup-ruby from 1.270.0 to 1.275.0 by itaybre in #7088
  • Bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 by dependabot in #7084
  • Bump actions/download-artifact from 6 to 7 by dependabot in #7048
  • Bump aws-sdk-s3 from 1.205.0 to 1.208.0 by dependabot in #7074
  • Bump ruby/setup-ruby from 1.269.0 to 1.270.0 by dependabot in #7049
  • Update clang-format version by github-actions in #7056
  • Bump actions/cache from 4 to 5 by dependabot in #7052
  • Bump actions/upload-artifact from 5 to 6 by dependabot in #7050
  • Bump codecov/codecov-action from 5.5.1 to 5.5.2 by dependabot in #7051

Other

  • (dx) Add structured Makefile with usage description by philprime in #7129
  • (release) Switch from action-prepare-release to Craft (minimal) by BYK in #7153
  • Update iOS test destination OS version to 18.5 in fast-pr-checks workflow by itaybre in #7168
  • Fix typos in comments in multiple files v2 by philipphofmann in #7139
  • Run visionOS tests on Cirrus Runners + Boot simulator by itaybre in #7147
  • Skip jobs/steps that require secrets for non contributors by itaybre in #7124
  • Add attributable protocol for typed attribute values by philprime in #7077
  • Allow alpha releases on RNSentry.podspec for Cross Platform Test by itaybre in #7130
  • Remove swift5.9 checks by itaybre in #7098
  • Remove duplicate file in project by itaybre in #7093
  • Convert SentryMetricKitIntegration to Swift by noahsmartin in #7076
  • Removes HybridSDK subspec by itaybre in #7019
  • Move testRemoveImageFromTail to flaky plan by itaybre in #7041
  • Use at least xcode 16 for all jobs by itaybre in #7012
  • Cleanup file filter for required files modified by itaybre in #7031
  • Remove assembly workflow files from UI test filter by itaybre in #7030
  • Bumps macOS-14 runner to macOS-15 by itaybre in #7029
  • Ensure required simulators are loaded for all platforms by itaybre in #7022

Other

  • test: Add Options Documentation Sync Tests by philipphofmann in #7075

🤖 This preview updates automatically when you update the PR.

@philprime philprime merged commit db9e223 into main Jan 15, 2026
196 checks passed
@philprime philprime deleted the philprime/metrics-envelope branch January 15, 2026 18:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dontmerge A branch that absolutely should not be merged while this label is applied. ready-to-merge Use this label to trigger all PR workflows

Projects

None yet

4 participants

Comments